package net.seriousinc.SeriousMobile;

import java.io.IOException;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.jibble.pircbot.IrcException;
import org.jibble.pircbot.NickAlreadyInUseException;
import org.jibble.pircbot.PircBot;
import org.jibble.pircbot.User;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.text.Html;
import android.text.Spanned;
import android.text.util.Linkify;
import android.text.util.Linkify.TransformFilter;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;



public class ZenoChatActivity extends Fragment {
	
	private static final String CHANNEL = "#seriousinc";
	private static final String SERVER = "irc.seriousinc.net";
	public static final SimpleDateFormat TIMEFORMAT = new SimpleDateFormat("HH:mm");
	public static boolean active;
	
	ChatInterface irc;
	String currentChannel;
	IRCHandler handler;
	EditText input;
	

	
	public boolean isConnected()
	{
		return irc.isConnected();
	}
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        active = true;
    }
    
    public View onCreateView(LayoutInflater inflater, ViewGroup root, Bundle savedInstanceState)
    {
    	View v = inflater.inflate(R.layout.chat, root, false);
        //ProgressDialog dialog = ProgressDialog.show(this, null, "Signing in...", true);
    	input = ((EditText)v.findViewById(R.id.inputField));
    	
    	class DoneOnEditorActionListener implements OnEditorActionListener {
    	    @Override
    	    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
    	        if (actionId == EditorInfo.IME_ACTION_DONE) {
    	            //InputMethodManager imm = (InputMethodManager)v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
    	            //imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
    	            submitMessage(v);
    	            return true;	
    	        }
    	        return false;
    	    }
    	}
    	input.setOnEditorActionListener(new DoneOnEditorActionListener());
    	
        return v;
    }
    
    @Override
    public void onStart()
    {
    	super.onStart();
    	if(irc == null || !irc.isConnected())
    	{
	    	String nick = getArguments().getString("nick");
	    	String nickserv = getArguments().getString("nickserv");
	    	
	    	connect(nick, nickserv);
    	}
    }
    public void connect(String nick, String nickserv)
    {
    	
        String channel = CHANNEL;
        String server = SERVER;
        View v = getView();
        
        TextView messageArea = ((TextView)v.findViewById(R.id.messageArea));
        //messageArea.setAutoLinkMask(Linkify.ALL);
        ScrollView scroller = ((ScrollView)v.findViewById(R.id.scroller));
        LinearLayout tabbar = ((LinearLayout)v.findViewById(R.id.tabbar));
        handler = new IRCHandler(messageArea, scroller, tabbar);
        //handler.setChannel(channel);
        ViewTreeObserver observer = v.getViewTreeObserver();
    	observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
			
			@Override
			public void onGlobalLayout() {
				// TODO Auto-generated method stub
				handler.scrollDown();
			}
		});
        final EditText edittext = (EditText) v.findViewById(R.id.inputField);
        edittext.setOnKeyListener(new OnKeyListener() {
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                // If the event is a key-down event on the "enter" button
                if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
                    (keyCode == KeyEvent.KEYCODE_ENTER)) {
                  submitMessage(v);
                  return true;
                }
                return false;
            }
        });
        
        irc = new ChatInterface(nick, nickserv, handler);
       // irc.setDialog(dialog);
        irc.setAutoNickChange(true);
        
        try {
        	currentChannel = channel;
			irc.connect(server);
			joinChannel(channel);
			
		} catch (NickAlreadyInUseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IrcException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
       // dialog.dismiss();
    }
    
    public void printUserList()
    {
    	User[] users = irc.getUsers(CHANNEL);
    	MessageFormat fmt = new MessageFormat("<font color=\"#00FFFF\">{0}</font>");
    	if(users!=null)
    	{
    	Object[] args = {users.length+ " users:"};
    	handler.appendMessage(Html.fromHtml(fmt.format(args)));
    	for(User u : users)
    	{
    		 args = new Object[]{"\t"+u.getNick()};
    		handler.appendMessage(Html.fromHtml(fmt.format(args)));
    	}
    	}
    }
    
    @Override
    public void onDestroy()
    {
    	super.onDestroy();
    	irc.quitting = true;
    	irc.disconnect();
    	active = false;
    }
    
    public void joinChannel(String channel)
    {
    	irc.joinChannel(channel);
    	handler.setChannel(channel);
    }
    
    public void submitMessage(View v)
    {
    	String message = input.getText().toString();
    	input.setText("");
    	if(message.length() > 0)
    	{
	    	if(message.startsWith("/"))
	    	{

	    	}
	    	else{
		    	irc.sendMessage(currentChannel, message);
		    	
		    	
				irc.onMessage(currentChannel, irc.getNick(), irc.getLogin(), "", message);
	    	}
    	}
    }
    
    class IRCHandler extends Handler
    {
    	
		TextView messageArea;
    	ScrollView scroller;
    	LinearLayout tabbar;
    	Map<String, ChatHistory> history;
    	Map<String, Button> channelButtons;
    	//String currentchannel;
    	
    	
    	
    	public IRCHandler(TextView messageArea, ScrollView scroller, LinearLayout tabbar)
    	{
    		this.messageArea = messageArea;
    		this.scroller = scroller;
    		history = new HashMap<String, ChatHistory>();
    		channelButtons = new HashMap<String, Button>();
    		this.tabbar = tabbar;
    		//updatePadding();
    		
    		
    	}
    	
    	public void updatePadding()
    	{
    		int padding = scroller.getHeight();
    		padding -= messageArea.getLineCount()*messageArea.getLineHeight();
    		padding = Math.max(padding, 0);
    		Log.i("padding", ""+padding);
    		messageArea.setPadding(0, padding, 0, 0);
    		scrollDown();
    	}

		public void scrollDown() {
			scroller.post(new Runnable()
		    {
		        public void run()
		        {
		            scroller.fullScroll(View.FOCUS_DOWN);
		        }
		    });
		}
    	
    	@Override
    	public void handleMessage(Message msg)
    	{
    		Bundle data = msg.getData();
    		
    		Log.i("handleMessage", data.getString("message"));
    		Log.i("time=", TIMEFORMAT.format(new Date(System.currentTimeMillis())));
    		
    		String channel = data.getString("channel");
    		
    		if(channel.equalsIgnoreCase("NOTICE") || channel.equalsIgnoreCase("Global") || channel.equalsIgnoreCase("NickServ") || channel.equalsIgnoreCase("HostServ"))
    			channel = "Status";
    		if(data.containsKey("LEAVE"))
    		{
    			leaveChannel(channel);
    			channel = "Status";
    		}
    		String message = data.getString("message");
    		addChannel(channel);
    		
    		history.get(channel).addMessage(Html.fromHtml(message));
    		//createFormattedMessage();
    		if(channel.equalsIgnoreCase(currentChannel))
    		{
    			appendMessage(Html.fromHtml(message));
    			
    		}
    		
    	}

    	private void leaveChannel(String channel)
    	{
    		if(channel.equalsIgnoreCase("Status") && channelButtons.containsKey(channel))
    		{
    			setChannel("Status");
    			tabbar.removeView(channelButtons.get(channel));
    			
    		}
    	}
    	
    	
    	private void addChannel(String channel) {
    		if(!history.containsKey(channel))
    		{
    			history.put(channel, new ChatHistory());
    			
    		}
    		if(!channelButtons.containsKey(channel))
    		{
    			Button channelButton = new Button(tabbar.getContext());
    			channelButtons.put(channel, channelButton);
    			channelButton.setText(channel);
    			channelButton.setOnClickListener(new OnClickListener() {
    				String channel;
    				@Override
    				public void onClick(View v) {
    					// TODO Auto-generated method stub
    					setChannel(channel);
    				}
    				
    				public OnClickListener setThisChannel(String channel)
    				{
    					this.channel = channel;
    					return this;
    				}
    			}.setThisChannel(channel));
    			tabbar.addView(channelButton);
    		}
    	}
    	
    	public void setChannel(String channel)
    	{
    		addChannel(channel);
    		if(currentChannel == null || !currentChannel.equalsIgnoreCase(channel))
    		{
    			currentChannel = channel;
    			messageArea.setText("");
    			LinkedList<Spanned> messages = history.get(channel).messages;
    			for(Spanned m: messages)
    			{
    				appendMessage(m);
    			}
    		}
    		
    		
    	}
    	
    	
    	

		private void appendMessage(Spanned m) {
			messageArea.append(m);
			messageArea.append("\n");
			Linkify.addLinks(messageArea, Linkify.WEB_URLS);
			
			TransformFilter mentionFilter = new TransformFilter() {
		        public final String transformUrl(final Matcher match, String url) {
		        	Log.i("match", match.group(2));
		            return match.group(2);
		        }
		    };
			
			Pattern twitMatcher = Pattern.compile("(\\s)@([A-Za-z]+[A-Za-z0-9]+)");
			String twitURL = "http://twitter.com/";
			Linkify.addLinks(messageArea, twitMatcher, twitURL, null, mentionFilter);
			
			//updatePadding();
			scrollDown();
			
		}

    	
    }
    
    
    
    @Override
    public void onConfigurationChanged(Configuration newConfig)
    {
    	super.onConfigurationChanged(newConfig);
    	//handler.updatePadding();
    	handler.scrollDown();
    }
}

class ChatHistory
{
	LinkedList<Spanned> messages;
	int historylength = 500;
	public ChatHistory()
	{
		messages = new LinkedList<Spanned>();
	}
	
	public void addMessage(Spanned message)
	{
		messages.add(message);
		if (historylength > 0 && messages.size() > historylength)
			messages.removeFirst();
	}
	
	public void setHistoryLength(int length)
	{
		historylength = length;
	}
	
}


class ChatInterface extends PircBot
{
	private static final SimpleDateFormat TIMEFORMAT = new SimpleDateFormat("HH:mm");
	boolean quitting;
	//TextView textArea;
	//static MessageFormat format = new MessageFormat("<{1}> {2}\n");
	MessageFormat msgFormat = new MessageFormat("[{0}] &lt;{1}&gt; {2}");
	MessageFormat modeFormat = new MessageFormat("[{0}] * {1} sets mode: {2}");
	MessageFormat joinFormat = new MessageFormat("<font color=\"#00FF00\">[{0}] * {1} ({2}@{3}) has joined {4}</font>");
	MessageFormat quitFormat = new MessageFormat("<font color=\"#990000\">[{0}] * {1} ({2}@{3}) has quit ({4})</font>");
	MessageFormat noticeFormat = new MessageFormat("<font color=\"#A52A2A\">[{0}] &lt;{1}&gt; {2}</font>");
	MessageFormat actionFormat = new MessageFormat("[{0}] {1} {2}");
	MessageFormat partFormat = new MessageFormat("[{0}] * {1} ({2}@{3}) has left {4}.");
	MessageFormat nickFormat = new MessageFormat("[{0}] * {1} is now known as {2}");
	MessageFormat kickFormat = new MessageFormat("[{0}] * {1} has kicked {2} ({3})");
	MessageFormat discFormat = new MessageFormat("<font color=\"#990000\">[{0}] * {1}</font>");
	
	//LinkedList<String> messageQueue;
	Handler handler;
	
	Map<String, LinkedList<String>> users;
	ProgressDialog dialog;
	String nickserv;
	
	public static String formatMessage(String message) {
		
		String replaced = StringUtils.replaceEach(message, new String[]{"&", "\"", "<", ">", "\n"}, 
															new String[]{"&amp;", "&quot;", "&lt;", "&gt;", "<br/>"});
		//Log.i("chat", replaced);
		replaced = replaced.replaceAll("(\\d\\d?),(\\d\\d?)", "$1");

		String[] colors = {"12", 
				"5", "11", "2", "14", "3", "9", "15", 
				"13", "7", "6", "4", "10", "16", "8","1", "", 
				"", "", ""};
		String[] colorSpans = {"<font color=\"#0000FF\">","<font color=\"#A52A2A\">", "<font color=\"#00FFFF\">",
				"<font color=\"#00008B\">", "<font color=\"#A1A1A1\">", "<font color=\"#008000\">", "<font color=\"#00FF00\">",
				"<font color=\"#D3D3D3\">", "<font color=\"#FF00FF\">", "<font color=\"#808000\">", "<font color=\"#800080\">", 
				"<font color=\"#FF0000\">", "<font color=\"#008080\">", "<font color=\"#FFFFFF\">", "<font color=\"#FFFF00\">", 
				"<font color=\"#000000\">","<b>", "</font>", "<font text-decoration:underline\">", ""};
		replaced = StringUtils.replaceEach(replaced, colors, colorSpans);
		replaced = replaced.replaceAll("(\\d\\d?)", "");
		//Log.i("chat", replaced);
		//Object[] args = {msg.timestamp, sender, replaced};
		return replaced;
	}
	
	public void setDialog(ProgressDialog dialog) {
		// TODO Auto-generated method stub
		this.dialog = dialog;
	}

	public ChatInterface(String name, String nickserv, Handler handler)
	{
		this.setName(name);
		this.setLogin(name);
		//this.textArea = textArea;
		this.handler = handler;
		this.nickserv = nickserv;
		users = new HashMap<String, LinkedList<String>>();
		
		//messageQueue = new LinkedList<String>();
	}
	
	@Override
	public void onConnect()
	{
		if(dialog != null)
			dialog.dismiss();
		
		sendRawLineViaQueue("MOTD");
		if(nickserv.length() > 0)
		{
			sendMessage("NickServ", "identify "+nickserv);
		}
		joinChannel("#seriousinc");
	}
	
	@Override
	public void onUnknown(String line)
	{
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		data.putString("channel", "GLOBAL");
		Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), line};
		data.putString("message", new MessageFormat("[{0}] {1}").format(args));
		mes.setData(data);
		handler.sendMessage(mes);
	}
	
	@Override
	public void onUserList(String channel, User[] userList)
	{
		if(!users.containsKey(channel))
		{
			users.put(channel, new LinkedList<String>());
		}
		for(User u : userList)
		{
			users.get(channel).add(u.getNick());
		}
	}
	
	
	@Override
	public void onAction(String sender, String login, String hostname, String target, String action)
	{
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		if(target.equals(getNick()))
			target = sender;
		
		data.putString("channel", target);
		Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), sender, action};
		data.putString("message",actionFormat.format(args));
		data.putString("type", "MESSAGE");
		mes.setData(data);
		handler.sendMessage(mes);
	}
	
	@Override
	public void onNotice(String sender,String login,String hostname,String target,String notice)
	{
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		if(target.equals(getNick()))
			target = sender;
		notice = formatMessage(notice);
		data.putString("channel", target);
		Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), sender, notice};
		data.putString("message", noticeFormat.format(args));
		data.putString("type", "MESSAGE");
		mes.setData(data);
		handler.sendMessage(mes);
	}
	
	@Override
	public void onMode(String channel, String sender, String login, String hostname, String mode)
	{
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		data.putString("channel", channel);
		Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), sender, mode};
		data.putString("message", modeFormat.format(args));
		data.putString("type", "MESSAGE");
		mes.setData(data);
		handler.sendMessage(mes);
	}
	
	@Override
	public void onMessage(String channel, String sender, String login, String hostname, String message)
	{
		message = formatMessage(message);
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		data.putString("channel", channel);
		Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), sender, message};
		data.putString("message", msgFormat.format(args));
		data.putString("type", "MESSAGE");
		mes.setData(data);
		handler.sendMessage(mes);
	}
	
	@Override
	public void onNickChange(String oldNick, String login, String hostname, String newNick) 
	{
		
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		LinkedList<String> channels = getChannels(oldNick);
		for(String channel : channels)
		{
			users.get(channel).remove(oldNick);
			users.get(channel).add(newNick);
			data.putString("channel", channel);
			Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), oldNick, newNick};
			data.putString("message", nickFormat.format(args));
			data.putString("type", "MESSAGE");
			mes.setData(data);
			handler.sendMessage(mes);
		}
	}
	
	@Override
	public void onUserMode(String targetNick, String sourceNick, String sourceLogin, String sourceHostname, String mode) 
	{
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		LinkedList<String> channels = getChannels(targetNick);
		for(String channel : channels)
		{
			data.putString("channel", channel);
			Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), sourceNick, mode + " " + targetNick};
			data.putString("message", modeFormat.format(args));
			data.putString("type", "MESSAGE");
			mes.setData(data);
			handler.sendMessage(mes);
		}
	}
	
	@Override
	public void onKick(String channel, String kickerNick, String kickerLogin, String kickerHostname, String recipientNick, String reason) 
	{
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		if(recipientNick.equals(getNick()))
		{
			data.putString("LEAVE", channel);
		}

		reason = formatMessage(reason);
		data.putString("channel", channel);
		Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), kickerNick, recipientNick, reason};
		data.putString("message", kickFormat.format(args));
		data.putString("type", "MESSAGE");
		mes.setData(data);
		handler.sendMessage(mes);
		
	}
	
	@Override
	public void onPrivateMessage(String sender, String login, String hostname, String message)
	{
		message = formatMessage(message);
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		data.putString("channel", sender);
		Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), sender, message};
		data.putString("message", msgFormat.format(args));
		data.putString("type", "MESSAGE");
		mes.setData(data);
		handler.sendMessage(mes);
		
	}
	
	@Override
	public void onJoin(String channel, String sender, String login, String hostname)
	{
		if(!users.containsKey(channel))
			users.put(channel, new LinkedList<String>());
		users.get(channel).add(sender);
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		data.putString("channel", channel);
		Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), sender, login, hostname, channel};
		data.putString("message", joinFormat.format(args));
		data.putString("type", "MESSAGE");
		mes.setData(data);
		handler.sendMessage(mes);
	}

	@Override
	public void onPart(String channel, String sender, String login, String hostname)
	{
		users.get(channel).remove(sender);
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		
		
		if(sender.equals(getNick()))
		{
			data.putString("LEAVE", channel);
		}
		data.putString("channel", channel);
		Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), sender, login, hostname, channel};
		data.putString("message", partFormat.format(args));
		data.putString("type", "MESSAGE");
		mes.setData(data);
		handler.sendMessage(mes);
	}
	
	@Override
	public void onQuit(String sender, String login, String hostname, String reason)
	{
		reason = formatMessage(reason);
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		LinkedList<String> channels = getChannels(sender);
		for(String channel : channels)
		{
			data.putString("channel", channel);
			Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), sender, login, hostname, reason};
			data.putString("message", quitFormat.format(args));
			data.putString("type", "MESSAGE");
			mes.setData(data);
			handler.sendMessage(mes);
		}
	}
	
	@Override
	public void onDisconnect()
	{
		Message mes = Message.obtain();
		Bundle data = new Bundle();
		for(String channel : getChannels())
		{
			data.putString("channel", channel);
			Object[] args = {TIMEFORMAT.format(new Date(System.currentTimeMillis())), "Disconnected"};
			data.putString("message", discFormat.format(args));
			data.putString("type", "MESSAGE");
			mes.setData(data);
			handler.sendMessage(mes);
		}
		if(!quitting)
		{
			try {
				reconnect();
			} catch (NickAlreadyInUseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IrcException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	
	
	public LinkedList<String> getChannels(String nick)
	{
		LinkedList<String> channels = new LinkedList<String>();
		
		Set<String> keys = users.keySet();
		
		for(String key : keys)
		{
			if(users.get(key).contains(nick))
				channels.add(key);
		}

		return channels;
	}
	
}


